package serverComuncation;

import java.io.PrintWriter;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import bridges.BridgesCollection;
import bridges.CommunicationBridge;
import bridges.MsgAndId;
import database.DB;

public class Observer implements Runnable{
	
	private Vector<String> msgs;
	private Vector<PrintWriter> outs;
	public static Observer observer;
	private BridgesCollection activeUsers;
	private BridgesCollection unActiveUsers;
	private Vector<MsgAndId> newMsgs;
	private Vector<MsgAndId> refreshMsgs;
	private Object lock;
	
	
	public Observer() {
		this.msgs = new Vector<String>();
		outs = new Vector<PrintWriter>();
		this.activeUsers = new BridgesCollection();
		this.unActiveUsers = new BridgesCollection();
		this.refreshMsgs = new Vector<MsgAndId>();
		this.newMsgs = new Vector<MsgAndId>();
		lock = new Object();
	}
	
	public void connect(Long id , PrintWriter out, int port)
	{
		System.out.println("the port is " + port);
		if (port ==MultipleClientProtocolServer.port)
		{
			this.unActiveUsers.addBridge(id, new CommunicationBridge(out, id));
		}
		else if (port == MultipleClientProtocolServer.webPort)
		{
			this.unActiveUsers.addBridge(id, new WebCommunicationBridge(out,id));
		}
	}
	
	public void disconnect(Long id)
	{
		this.activeUsers.removeBridge(id);
		this.unActiveUsers.removeBridge(id);
	}
	
	public void activateUser(Long id,String userName)
	{
		changeUserMod(id, unActiveUsers, activeUsers,userName);
	}

	private void changeUserMod(Long id,BridgesCollection from,BridgesCollection to,String userName) {
		CommunicationBridge bridge = from.removeBridge(id);
		if (bridge != null)
		{
			bridge.setUserName(userName);
			to.addBridge(id, bridge);
		}
		
	}
	
	public void unActivateUser(Long id)
	{
		changeUserMod(id, activeUsers, unActiveUsers,null);
	}
	
	public void addNewOut(PrintWriter newOut, int port)
	{
		synchronized (outs) {
			outs.add(newOut);
			this.connect(Thread.currentThread().getId(), newOut,port);
		}
	}
	
	public void removeOut(PrintWriter delete)
	{
		synchronized (outs) {
			outs.remove(delete);
			this.disconnect(Thread.currentThread().getId());
		}
	}
	
	public void changeCurentPath(Long id,int subForumId)
	{
		CommunicationBridge bridge = getCommunicationBridge(id);
		bridge.setUserSubForum(subForumId);
	}

	private CommunicationBridge getCommunicationBridge(Long id) {
		CommunicationBridge bridge;
		if(this.activeUsers.contains(id))
		{
			bridge = activeUsers.getBridge(id);
		}
		else
		{
			bridge = unActiveUsers.getBridge(id);
		}
		return bridge;
	}
	
	public void changeCurentPathViewThread(long id, int threadId) {
		CommunicationBridge bridge = getCommunicationBridge(id);
		bridge.setThreadForum(threadId);
	}
	
	@Deprecated
	public static void addNewMsg(String msg)
	{
		synchronized (observer.msgs) {
			observer.msgs.add(msg + "\n" + "\0");
			observer.msgs.notifyAll();
		}
	}
	
	public static void addNewMsg(String msg,String webMsg,int subForumId)
	{
		addNewMsgsToVector(msg,webMsg, subForumId, observer.newMsgs,false);
	}
	
	public static void addNewRefreshMsg(String msg,String webMsg,int subForumId,boolean isSubForum)
	{
		addNewMsgsToVector(msg,webMsg, subForumId, observer.refreshMsgs,isSubForum);
	}
	
	private static void addNewMsgsToVector(String msg,String webMsg,int subForumId,Vector<MsgAndId> vec,boolean isSubForum)
	{
		synchronized (observer.lock)
		{
			vec.add(new MsgAndId(subForumId, msg,webMsg,isSubForum));
			observer.lock.notifyAll();
		}
	}



	@Override
	public void run() {
		
		try
		{
			while (true)
			{
//				oldRun();
				newRun();
			}
		}
		catch (InterruptedException e)
		{
			
		}
	}

	private void newRun()throws InterruptedException 
	{
		synchronized (this.lock)
		{
			if (this.newMsgs.size() == 0 && this.refreshMsgs.size() ==0)
			{
				this.lock.wait();
			}
			else
			{
				if (this.newMsgs.size() != 0)
				{
					sendAllNewMsgs();
				}
				if (this.refreshMsgs.size() != 0)
				{
					sendAllRefreshMsgs();
				}
			}
		}
	}

	private void sendAllRefreshMsgs() 
	{
		synchronized (this.refreshMsgs)
		{
			for (MsgAndId msgAndId : this.refreshMsgs) 
			{
				Vector<CommunicationBridge> bridges = getAllOutInPath(msgAndId.getSubForumId(),msgAndId.isSubForum());
				for (CommunicationBridge bridge : bridges) {
					bridge.send(msgAndId);
				}
			}
			this.refreshMsgs.clear();
		}
	}

	private Vector<CommunicationBridge> getAllOutInPath(int subForumId,boolean isSubForum) {
		Vector<CommunicationBridge> outs = new Vector<CommunicationBridge>();
		outs = getOuts(outs,this.activeUsers,subForumId,isSubForum);
		outs.addAll(getOuts(outs,this.unActiveUsers,subForumId,isSubForum));
		return outs;
	}

	private Vector<CommunicationBridge> getOuts(Vector<CommunicationBridge> outs,BridgesCollection bridges,
			int subForumId,boolean isSubForum) {
		return bridges.getAllOutsInSubForum(subForumId,isSubForum);
	}

	private void sendAllNewMsgs()
	{
		synchronized (this.newMsgs)
		{
			for (MsgAndId msgAndId : newMsgs) 
			{
				Map<String,Boolean> userNames = DB.getAllWritersInSubForum(msgAndId.getSubForumId());
				Set<CommunicationBridge> bridges = this.activeUsers.getAllOuts(userNames);
				for (CommunicationBridge bridge : bridges)
				{
					bridge.send(msgAndId);
				}
			}
			newMsgs.clear();
		}
	}

	private void oldRun() throws InterruptedException {
		synchronized (msgs) {
			if (msgs.size() == 0)
			{
				this.msgs.wait();
			}
			else
			{
				sendAllMsgs();
			}
		}
	}

	private void sendAllMsgs() {
		synchronized (outs) {
			for (String msg : msgs) {
				for (PrintWriter out : outs) {
					out.println(msg);
				}
				
			}
			msgs.clear();
		}
	}

	public static int getNumOfConnection() {
		int ans =0;
		synchronized (observer.outs)
		{
			ans = observer.outs.size();
		}
		return ans;
	}



	
	
	

}
